import {
  Component,
  OnInit,
  HostBinding,
  Input,
  forwardRef,
  ElementRef,
  HostListener,
  OnDestroy,
  AfterContentChecked,
  ViewChild,
  Renderer2,
  ContentChildren,
  QueryList,
  ContentChild,
  Query,
} from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';
import {animate, state, style, transition, trigger} from '@angular/animations';

import { OverlayEntry, POSITION_MAP } from '../shared/shared';
import { SelectStoreService, SelectMode } from './select-store.service';
import { SelectOptionComponent } from './select-option.component';
import { SelectOptionAllComponent } from './select-option-all.component';

@Component({
  selector: 'fui-select',
  templateUrl: './select.component.html',
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => SelectComponent),
      multi: true,
    },
    SelectStoreService,
  ],
  animations: [
    trigger('showHide', [
      state('void', style({
        transform: 'scaleY(0.8)',
        opacity: 0,
      })),
      state('true', style({
        opacity: 1,
        transform: 'scaleY(1)',
      })),
      transition('void => *', animate('120ms cubic-bezier(0, 0, 0.2, 1)')),
      transition('* => void', animate('100ms 25ms linear', style({opacity: 0}))),
    ]),
  ],
})
export class SelectComponent extends OverlayEntry implements ControlValueAccessor, OnInit, OnDestroy, AfterContentChecked {

  /** Placeholder. */
  @Input() placeholder = '';

  /** Search keyword. */
  @Input() searchKey = '';

  /** Expand direction. */
  @Input() direction: 'top' | 'bottom' = 'bottom';

  /** Loading status */
  @Input() loading = false;

  /** 是否disabled */
  private innerDisabled = false;

  /** 是否readonly */
  private innerReadonly = false;

  /** 是否允许搜索 */
  private innerAllowSearch = false;

  /** 是否允许清除 */
  private innerAllowClear = false;

  /** 是否处在输入状态 */
  isComposing = false;

  /** 搜索过滤 */
  searchFilter = '';

  hostWidth: number;

  @ViewChild('fuiSelectOptions') fuiSelectOptions: ElementRef;
  @ViewChild('fuiSelectNoOptions') fuiSelectNoOptions: ElementRef;

  @ContentChild(SelectOptionAllComponent) optionAllComponent: Query;

  @ContentChildren(SelectOptionComponent) optionListComponent: QueryList<SelectOptionComponent>;

  @HostBinding('class.fui-select') true;
  @HostBinding('class.fui-select-allow-clear') get allowClearClass() {
    return this.allowClear;
  }

  @HostBinding('class.open') get openClass() {
    return this.visible.getValue();
  }

  @HostBinding('attr.disabled') get attrDisabled() {
    return this.disabled;
  }

  @HostBinding('attr.readonly') get attrReadonly() {
    return this.readonly;
  }

  @HostBinding('attr.tabindex') get tabIndex() {
    return this.disabled ? null : 0;
  }

  @HostListener('blur') onBlur() {
    this.controlValueAccessorTouchFn();
  }

  /** Disable state. */
  @Input() set disabled(value: boolean) {
    this.innerDisabled = !(value === false);
  }

  get disabled() {
    return this.innerDisabled;
  }

  /** Readonly state. */
  @Input() set readonly(value: boolean) {
    this.innerReadonly = !(value === false);
  }

  get readonly() {
    return this.innerReadonly;
  }

  /** Allow search function. */
  @Input() set allowSearch(value: boolean) {
    this.innerAllowSearch = !(value === false);
  }

  get allowSearch() {
    return this.innerAllowSearch;
  }

  /** Allow clear selection function. */
  @Input() set allowClear(value: boolean) {
    this.innerAllowClear = !(value === false);
  }

  get allowClear() {
    return this.innerAllowClear;
  }

  /** Select mode, support 'single', 'multiple'. Default to 'single'. */
  @Input() set mode(value: SelectMode) {
    if (value) {
      this.store.mode = value;
    }
  }

  public controlValueAccessorChangeFn: (value: any) => void = () => { };
  public controlValueAccessorTouchFn: () => void = () => { };

  constructor(
    public elementRef: ElementRef,
    public store: SelectStoreService,
    public renderer: Renderer2,
  ) {
    super();
    this.setOverlayOrigin(this);
  }

  ngOnInit() {
    this.setPosition(this.direction);
    const dirs = ['bottom', 'top'];
    if (this.direction === 'top') {
      dirs.reverse();
    }
    this.positions = dirs.map((dir) => POSITION_MAP[dir]);

    this.store.selectOptionChange.subscribe((event) => {
      // 只在单选和用户选中事件关闭下拉
      if (event.isUserEvent && this.store.mode === 'single') {
        this.hide();
      }
    });

    this.visible.subscribe((visible: boolean) => {
      if (this.allowSearch) {
        // visible状态改变，清空搜索
        this.searchFilter = '';
      }
    });

    this.store.selectValuesChange.subscribe((values) => {
      this.controlValueAccessorChangeFn(values);
    });
  }

  ngOnDestroy() {
    this.store.destroy();
  }

  ngAfterContentChecked() {
    // TODO: cdk auto calc width
    // https://github.com/angular/material2/issues/10393
    // 只在下拉开启状况下更新hostWidth，减少对dom的访问
    if (this.openClass) {
      const currentHostWidth = this.elementRef.nativeElement.offsetWidth;
      if (this.hostWidth !== currentHostWidth) {
        this.hostWidth = currentHostWidth;
      }
    }
  }

  clickHead() {
    if (this.disabled || this.readonly) {
      return;
    }

    this.show();
  }

  clear(event: MouseEvent) {
    event.stopPropagation();
    this.store.clear();
  }

  writeValue(value: any) {
    if (this.store.mode === 'multiple') {
      this.store.selectByValues([].concat(value));
    } else {
      this.store.selectByValues([value]);
    }
  }

  toggleIcon() {
    return this.visible.getValue() ? 'angle-up' : 'angle-down';
  }

  registerOnChange(fn: (value: any) => void) {
    this.controlValueAccessorChangeFn = fn;
  }

  registerOnTouched(fn: () => void) {
    this.controlValueAccessorTouchFn = fn;
  }

  setDisabledState(isDisabled: boolean) {
    this.disabled = isDisabled;
  }

  setPosition(direction: string) {
    const operateEl = this.store.optionCount ? this.fuiSelectOptions : this.fuiSelectNoOptions;
    const transformOrigin = direction === 'top' ? '0 100%' : '0 0';
    if (operateEl) {
      this.renderer.setStyle(operateEl.nativeElement, 'transformOrigin', transformOrigin);
    }
  }

}
